home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
WASTE 1.2
/
WASTE Demo ƒ
/
WEDemoWindows.c
< prev
Wrap
Text File
|
1996-05-19
|
22KB
|
951 lines
/*
WASTE Demo Project:
Window Handling
Copyright © 1993-1996 Marco Piovanelli
All Rights Reserved
C port by John C. Daub
*/
#ifndef __ALIASES__
#include <Aliases.h>
#endif
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
#ifndef __FILETYPESANDCREATORS__
#include <FileTypesAndCreators.h>
#endif
#ifndef _LongCoords_
#include "LongCoords.h"
#endif
#ifndef __WEDEMOAPP__
#include "WEDemoIntf.h"
#endif
// some consts used by DoGrow()
enum {
kMinWindowWidth = 200,
kMinWindowHeight = 80
};
// static variables
static long sScrollStep; // how many pixels to scroll (used by ScrollProc)
static void CalcGrowIconRect( WindowRef window, Rect *iconRect )
{
Rect portRect = GetWindowPort( window )->portRect;
iconRect->top = portRect.bottom - (kBarWidth - 2);
iconRect->left = portRect.right - (kBarWidth - 2);
iconRect->bottom = portRect.bottom;
iconRect->right = portRect.right;
}
static void CalcTextRect( WindowRef window, Rect *textRect )
{
Rect portRect = GetWindowPort( window )->portRect;
textRect->top = 0;
textRect->left = 0;
textRect->bottom = portRect.bottom - (kBarWidth - 1);
textRect->right = portRect.right - (kBarWidth - 1);
InsetRect( textRect, kTextMargin, kTextMargin );
}
static void CalcScrollBarRect( WindowRef window, VHSelect axis, Rect *barRect )
{
Rect portRect = GetWindowPort( window )->portRect;
switch ( axis )
{
case v:
barRect->top = -1;
barRect->left = portRect.right - (kBarWidth - 1);
barRect->bottom = portRect.bottom - (kBarWidth - 2);
barRect->right = portRect.right + 1;
break;
case h:
barRect->top = portRect.bottom - (kBarWidth - 1);
barRect->left = -1;
barRect->bottom = portRect.bottom + 1;
barRect->right = portRect.right - (kBarWidth - 2 );
break;
}
}
/*
the standard Toolbox trap _DrawGrowIcon draws two lines from the grow icon
to the left and top margins of the window's content area
these additional lines may create ugly dirt, so we use this routine to temporarily
set the clip region to the grow icon rect.
in addition, if validate is true, we call _ValidRect on the icon rect
*/
static void MyDrawGrowIcon( WindowRef window, Boolean validate )
{
GrafPtr savePort;
RgnHandle saveClip;
Rect r;
// save port and set thePort to wind
GetPort( &savePort );
SetPortWindowPort( window );
// save the clip region
saveClip = NewRgn();
GetClip( saveClip );
// calculate the grow icon rect
CalcGrowIconRect( window, &r );
// set clip region to grow icon rect
ClipRect( &r );
// call _DrawGrowIcon
DrawGrowIcon( window );
// if validate is true, remove the grow icon rect from the update region
if ( validate )
ValidRect( &r );
// restore old clip region
SetClip( saveClip );
DisposeRgn( saveClip );
// restore old port
SetPort( savePort );
}
static void ScrollBarChanged( WindowRef window )
{
// scroll text to reflect new scroll bar setting
DocumentHandle hDocument = GetWindowDocument( window );
WEReference we;
LongRect viewRect, destRect;
we = (*hDocument)->we;
WEGetViewRect( &viewRect, we );
WEGetDestRect( &destRect, we );
WEScroll
(
viewRect.left - destRect.left - LCGetValue( (*hDocument)->scrollBars[ h ] ),
viewRect.top - destRect.top - LCGetValue( (*hDocument)->scrollBars[ v ] ),
we
);
}
static void AdjustBars( WindowRef window )
{
DocumentHandle hDocument;
WEReference we;
GrafPtr savePort;
LongRect viewRect, destRect;
long value;
long max;
ControlRef bar;
GetPort( &savePort );
SetPortWindowPort( window );
hDocument = GetWindowDocument(window);
we = (*hDocument)->we;
// get the view and destination rectangle
WEGetViewRect( &viewRect, we );
WEGetDestRect( &destRect, we );
// do the vertical axis
// get scroll bar handle
bar = (*hDocument)->scrollBars[ v ];
// calculate new scroll bar settings
// NOTE: (destRect.bottom - destRect.top) always equals the total text height because
// WASTE automatically updates destRect.bottom whenever line breaks are recalculated
value = viewRect.top - destRect.top;
max = value + (destRect.bottom - viewRect.bottom);
// make sure max is always non-negative
if ( max <= 0 )
max = 0;
// reset the scroll bar
LCSetMax( bar, max );
LCSetValue( bar, value );
// if value exceeds max then the bottom of the destRect is above
// the bottom of the view rectangle: we need to scroll the text downward
if ( value > max )
ScrollBarChanged( window );
// now do the horizontal axis
// get scroll bar handle
bar = (*hDocument)->scrollBars[ h ];
// calculate new scroll bar settings
value = viewRect.left - destRect.left;
max = value + (destRect.right - viewRect.right);
// make sure max is always non-negative
if ( max <= 0 )
max = 0;
// reset the scroll bar
LCSetMax( bar, max );
LCSetValue( bar, value );
SetPort( savePort );
}
static void ViewChanged( WindowRef window )
{
DocumentHandle hDocument;
GrafPtr savePort;
ControlRef bar;
Rect r;
LongRect viewRect;
VHSelect axis;
GetPort( &savePort );
SetPortWindowPort( window );
hDocument = GetWindowDocument( window );
// resize the text area
CalcTextRect( window, &r );
WERectToLongRect( &r, &viewRect );
WESetViewRect( &viewRect, (*hDocument)->we );
// move and resize the control bars
for ( axis = v; axis <= h; axis++ )
{
bar = (*hDocument)->scrollBars[ axis ];
CalcScrollBarRect( window, axis, &r );
MoveControl( bar, r.left, r.top );
SizeControl( bar, r.right - r.left, r.bottom - r.top );
ValidRect( &r );
}
// reset the thumb positions and the max values of the control bars
AdjustBars( window );
// redraw the control bars
ShowControl( (*hDocument)->scrollBars[ v ] );
ShowControl( (*hDocument)->scrollBars[ h ] );
SetPort( savePort );
}
/*
This is a deviation from the original Pascal WASTE Demo App code.
This "morally correct" code for window dragging is per an article in MacTech
Magazine (July 1994, Vol 10, No. 7). by Eric Shapiro (of Rock Ridge Enterprises)
called "Multiple Monitors vs. Your Application"
Eric addressed numerous things to allow your app to deal nicely with multiple
monitor setups, one of them is dragging.
According to Eric, many apps don't let you drag windows to second monitors, and
though holding down the cmd/opt keys often overrides this problem, it should
still be updated. And the only reason qd.screenBits.bounds works to allow
you to drag to second monitors is because of a kludge Apple put in the Window Manager
So, this is some code from Eric to make our app be "morally correct" :)
*/
void DoDrag( Point thePoint, WindowRef window )
{
Rect limitR;
if ( gHasColorQD )
limitR = ( **GetGrayRgn()).rgnBBox;
else
limitR = qd.screenBits.bounds;
DragWindow( window, thePoint, &limitR );
}
void Resize( Point newSize, WindowRef window )
{
DocumentHandle hDocument;
GrafPtr savePort;
Rect r;
RgnHandle tempRgn, dirtyRgn;
GetPort( &savePort );
SetPortWindowPort( window );
hDocument = GetWindowDocument( window );
// create temporarty regions for calculations
tempRgn = NewRgn();
dirtyRgn = NewRgn();
// save old text region
CalcTextRect( window, &r );
RectRgn( tempRgn, &r );
// erase the old grow icon rect
CalcGrowIconRect( window, &r );
EraseRect( &r );
// hide the scroll bars
HideControl( (*hDocument)->scrollBars[ v ] );
HideControl( (*hDocument)->scrollBars[ h ] );
// perform the actual resizing of the window, redraw scroll bars and grow icon
SizeWindow( window, newSize.h, newSize.v, false );
ViewChanged( window );
MyDrawGrowIcon( window, true );
// calculate the dirty region (to be updated)
CalcTextRect( window, &r );
RectRgn( dirtyRgn, &r );
XorRgn( dirtyRgn, tempRgn, dirtyRgn );
InsetRect( &r, -kTextMargin, -kTextMargin );
RectRgn( tempRgn, &r );
SectRgn( dirtyRgn, tempRgn, dirtyRgn );
// mark the dirty region as invalid
InvalRgn( dirtyRgn );
// throw away temporary regions
DisposeRgn( tempRgn );
DisposeRgn( dirtyRgn );
SetPort( savePort );
}
void DoGrow( Point hitPt, WindowRef window )
{
Rect sizeRect;
long newSize;
SetRect( &sizeRect, kMinWindowWidth, kMinWindowHeight, SHRT_MAX, SHRT_MAX );
if ( ( newSize = GrowWindow( window, hitPt, &sizeRect ) ) != 0L )
{
// for some reason, GrowWindow( ) returns a long value,
// but it's really a Point
Resize( * (Point *) &newSize, window );
}
}
void DoZoom( short partCode, WindowRef window )
{
DocumentHandle hDocument;
GrafPtr savePort;
Rect r;
GetPort( &savePort );
SetPortWindowPort( window );
hDocument = GetWindowDocument(window);
r = GetWindowPort( window )->portRect;
EraseRect( &r );
HideControl( (*hDocument)->scrollBars[ v ] );
HideControl( (*hDocument)->scrollBars[ h ] );
ZoomWindow( window, partCode, false );
ViewChanged( window );
CalcTextRect( window, &r );
InvalRect( &r );
SetPort( savePort );
}
// this is a callback tourine called by the Toolbox Control Manager
// move the scroll bar thumb and scroll the text accordingly
static pascal void ScrollProc( ControlRef bar, ControlPartCode partCode )
{
long value, step;
if ( partCode == kControlNoPart )
return;
value = LCGetValue( bar );
step = sScrollStep;
if ( (( value < LCGetMax( bar )) && ( step > 0 )) ||
(( value > 0 ) && ( step < 0 ) ) )
{
LCSetValue( bar, value + step );
ScrollBarChanged( FrontWindow( ) );
}
}
static void DoScrollBar( Point hitPt, EventModifiers modifiers, WindowRef window )
{
DocumentHandle hDocument;
ControlRef bar = nil;
LongRect viewRect;
ControlPartCode partCode;
long pageSize;
long step = 0;
#ifdef __cplusplus
static ControlActionUPP sScrollerUPP = NewControlActionProc( ScrollProc );
#else
static ControlActionUPP sScrollerUPP = nil;
if (sScrollerUPP == nil)
{
sScrollerUPP = NewControlActionProc( ScrollProc );
}
#endif
hDocument = GetWindowDocument( window );
WEGetViewRect( &viewRect, (*hDocument)->we );
// find out which control was hit (if any) and in which part
partCode = FindControl( hitPt, window, &bar );
// if any control was hit, it must be one of our two scroll bars:
// find out which and calculate the page size for it
if ( bar == (*hDocument)->scrollBars[ v ] )
{
pageSize = viewRect.bottom - viewRect.top;
}
else if ( bar == (*hDocument)->scrollBars[ h ] )
{
pageSize = viewRect.right - viewRect.left;
}
else
{
return; // return immediately if none of our scrollbars was hit
}
// dispatch on partCode
switch ( partCode )
{
case kControlIndicatorPart:
// click in thumb: call TrackControl with no actionProc and adjust text
TrackControl( bar, hitPt, nil );
LCSynch( bar );
ScrollBarChanged( window );
return;
case kControlUpButtonPart:
step = - ( ( modifiers & optionKey ) ? 1 : kScrollDelta );
break;
case kControlDownButtonPart:
step = + ( ( modifiers & optionKey ) ? 1 : kScrollDelta );
break;
case kControlPageUpPart:
step = - ( pageSize - kScrollDelta );
break;
case kControlPageDownPart:
step = + ( pageSize - kScrollDelta );
break;
} // switch
// save step in a static variable for our ScrollProc callback
sScrollStep = step;
// track the mouse
TrackControl( bar, hitPt, sScrollerUPP );
}
/*
This is a callback routine called whenever the text is scrolled automaticall.
Since auto-scrolling is enabled, WEScroll may be invoked internally by WASTE
in many different circumstances, and we want to be notified when this happens
so we can adjust the scroll bars
*/
static pascal void TextScrolled( WEReference we )
{
WindowRef window = nil;
// retrieve the window pointer stored in the WE instance as a "reference constant"
if ( WEGetInfo( weRefCon, &window, we ) != noErr )
return;
// make sure the scroll bars are in synch with the destination rectangle
AdjustBars( window );
}
Boolean DoContent( Point hitPt, const EventRecord *event, WindowRef window )
{
WEReference we;
Rect textRect;
GrafPtr savePort;
Boolean inBackground, handleClick, retval;
retval = false; // false means click should not activate this window
we = GetWindowWE(window);
// is this windowd in the background?
inBackground = ! IsWindowHilited( window );
// set the port to our window's port
GetPort( &savePort );
SetPortWindowPort( window );
// convert the point to local coordinates
GlobalToLocal( &hitPt );
// a click in an inactive window should normally activate it,
// but the availability of the Drag Manager introduces an exception to this rule:
// a click in the background selection may start a drag gesture,
// without activating the window
if ( inBackground )
{
if ( gHasDragAndDrop )
{
long selStart, selEnd;
RgnHandle selRgn;
WEGetSelection( &selStart, &selEnd, we );
selRgn = WEGetHiliteRgn( selStart, selEnd, we );
handleClick = PtInRgn( hitPt, selRgn ) && WaitMouseMoved( event->where );
DisposeRgn( selRgn );
}
else
{
handleClick = false; // no DragManager: never click-through
}
}
else
{
handleClick = true; // window is frontmost: always handle click
}
if ( handleClick )
{
CalcTextRect( window, &textRect );
if ( PtInRect( hitPt, &textRect ) )
{
WEClick( hitPt, event->modifiers, event->when, we );
}
else
{
DoScrollBar( hitPt, event->modifiers, window );
}
}
else
{
retval = inBackground;
}
// restore the port
SetPort( savePort );
return retval;
}
static void DoScrollKey( short keyCode, WindowRef window )
{
DocumentHandle hDocument;
ControlRef bar;
long value;
LongRect viewRect;
hDocument = GetWindowDocument(window);
bar = (*hDocument)->scrollBars[ v ];
// get current scroll bar value
value = LCGetValue( bar );
// get text view rect
WEGetViewRect( &viewRect, (*hDocument)->we );
switch ( keyCode )
{
case keyPgUp:
value -= ( viewRect.bottom - viewRect.top ) - kScrollDelta;
break;
case keyPgDn:
value += ( viewRect.bottom - viewRect.top ) - kScrollDelta;
break;
case keyHome:
value = 0;
break;
case keyEnd:
value = LONG_MAX;
break;
} // switch
// set the new scroll bar value and scroll the text pane accordingly
LCSetValue( bar, value );
ScrollBarChanged( window );
}
void DoKey( short key, const EventRecord *event )
{
WindowRef window;
short keyCode;
// do nothing if no window is active
if ( ( window = FrontWindow( ) ) == nil )
return;
// extract virtual key code from event record
keyCode = ( event->message & keyCodeMask ) >> 8 ;
// page movement keys are handled by DoScrollKey()
switch ( keyCode )
{
case keyPgUp:
case keyPgDn:
case keyHome:
case keyEnd:
DoScrollKey( keyCode, window );
break;
default:
WEKey( key, event->modifiers, GetWindowWE(window) );
break;
}
}
void DoUpdate( WindowRef window )
{
GrafPtr savePort;
RgnHandle updateRgn;
// if we have no windows, there's nothing to update!
if ( window == nil )
return;
// save the old drawing port
GetPort( &savePort );
SetPortWindowPort( window );
// notify everything that we're doing an update.
BeginUpdate( window );
// BeginUpdate sets the window port visRgn to the region to update
updateRgn = GetWindowPort(window)->visRgn;
if ( !EmptyRgn( updateRgn ) ) // if it's not an empty region, let's update it!
{
// erase the update region
EraseRgn( updateRgn );
// draw scroll bars
UpdateControls( window, updateRgn );
// draw grow icon
MyDrawGrowIcon( window, false );
// draw text
WEUpdate( updateRgn, GetWindowWE( window ) );
}
// tell everything we're done updating
EndUpdate( window );
// restore the old graphics port
SetPort( savePort );
}
void DoActivate( Boolean activFlag, WindowRef window )
{
DocumentHandle hDocument;
WEReference we;
GrafPtr savePort;
Rect barRect;
ControlPartCode barHilite;
short menuID;
VHSelect axis;
// if this is not one of our document windows, nothing to do here...
if ( ( hDocument = GetWindowDocument(window) ) == nil )
return;
we = (*hDocument)->we;
// set up the port
GetPort( &savePort );
SetPortWindowPort( window );
// activate or deactivate the text (and any other relevant stuff) depending on just
// what we're doing here...
if ( activFlag )
{
WEActivate( we );
barHilite = kControlNoPart;
}
else
{
WEDeactivate( we );
barHilite = kControlDisabledPart;
}
// redraw the grow icon (and validate its rect)
MyDrawGrowIcon( window, true );
// redraw the scroll bars with the new highlighting (and validate their rects)
for ( axis = v; axis <= h; axis++ )
{
HiliteControl( (*hDocument)->scrollBars[ axis ], barHilite );
CalcScrollBarRect( window, axis, &barRect );
ValidRect( &barRect );
}
// dim or undim text-related menus
for ( menuID = kMenuEdit; menuID <= kMenuFeatures; menuID++ )
{
if ( activeFlag )
EnableItem( GetMenuHandle( menuID ), 0 );
else
DisableItem( GetMenuHandle( menuID ), 0 );
}
// invalidate the menu bar
InvalMenuBar( );
// restore the old graphics port..
SetPort( savePort );
}
OSErr CreateWindow( const FSSpec *pFileSpec )
{
DocumentHandle hDocument = nil;
WindowRef window = nil;
AliasHandle alias = nil;
WEReference we = nil;
ControlRef bar = nil;
FInfo fileInfo;
Rect textRect;
LongRect longTextRect;
VHSelect axis;
OSErr err;
#ifdef __cplusplus
static WEScrollUPP sScrollerUPP = NewWEScrollProc( TextScrolled );
#else
static WEScrollUPP sScrollerUPP = nil;
if ( sScrollerUPP == nil )
{
sScrollerUPP = NewWEScrollProc( TextScrolled );
}
#endif
// allocate a relocateable block to hold a document record
hDocument = (DocumentHandle) NewHandleClear( sizeof( DocumentRecord ) );
if ( ( err = MemError( ) ) != noErr )
goto cleanup;
// create the window from a 'WIND' template: the window is initially invisible
// if ColorQuickDraw is available, create a color window
if ( gHasColorQD )
{
window = GetNewCWindow( kWindowTemplateID, nil, (WindowRef) -1L );
}
else
{
window = GetNewWindow( kWindowTemplateID, nil, (WindowRef) -1L );
}
// make sure we got a window
if ( window == nil )
{
err = memFullErr;
goto cleanup;
}
// link the document record to the window and the other way around
SetWRefCon(window, (long) hDocument);
(*hDocument)->owner = window;
// we got a window, so tell QuickDraw where to draw...
SetPortWindowPort( window );
// calculate the text rectangle
CalcTextRect( window, &textRect );
WERectToLongRect( &textRect, &longTextRect );
// create a new WASTE instance
if ( ( err = WENew( &longTextRect, &longTextRect, weDoAutoScroll +
weDoOutlineHilite +
weDoUndo +
weDoIntCutAndPaste +
weDoDragAndDrop +
weDoUseTempMem +
weDoDrawOffscreen, &we) ) != noErr )
goto cleanup;
// set the alignment to weFlushLeft so "slop recalc" is disabled
WESetAlignment( weFlushLeft, we );
// save a reference to the window in the WE instance
if ( ( err = WESetInfo( weRefCon, &window, we ) ) != noErr )
goto cleanup;
// now the other way around: save the WE reference in the document record
(*hDocument)->we = we;
// set up our scroll callback
if ( ( err = WESetInfo( weScrollProc, &sScrollerUPP, we ) ) != noErr )
goto cleanup;
// create the scroll bars from a control template
for ( axis = v; axis <= h; axis++ )
{
if ( ( bar = GetNewControl( kScrollBarTemplateID, window ) ) == nil )
{
err = memFullErr;
goto cleanup;
}
HiliteControl( bar, kControlDisabledPart );
// attach a LongControl record to the scroll bar: this allows us to use long
// settings and thus scroll text taller than 32,767 pixels
if ( ( err = LCAttach( bar ) ) != noErr )
goto cleanup;
// save control handle in the document record
(*hDocument)->scrollBars[ axis ] = bar;
} // for
// ViewChanged( ) adjusts the scroll bars rectangles to the window frame
ViewChanged( window );
// if pFileSpec is not nil, it points to a file to read, so let's read it!
if ( pFileSpec != nil )
{
// turn the cursor into a wristwatch because this can be a lengthy operation
SetCursor( * GetCursor( watchCursor ) );
// retrieve file infomation
if ( ( err = FSpGetFInfo( pFileSpec, &fileInfo ) ) != noErr )
goto cleanup;
// make sure we recognize the file type
if ( (fileInfo.fdType != kTypeText ) && ( fileInfo.fdType != ftSimpleTextDocument ) )
{
err = -1;
goto cleanup;
}
// read in the file
if ( ( err = ReadTextFile( pFileSpec, we ) ) != noErr )
goto cleanup;
// set the window title to the file name
SetWTitle( window, pFileSpec->name );
// create an alias to keep track of the file
if ( ( err = NewAlias( nil, pFileSpec, &alias ) ) != noErr )
goto cleanup;
(*hDocument)->fileAlias = (Handle) alias;
// if the file is a read-only file, go ahead and enable those flags
if (fileInfo.fdType == ftSimpleTextDocument )
{
WEFeatureFlag( weFReadOnly, weBitSet, we );
}
// let's make sure the cursor is happy...
InitCursor( );
}
// adjust scroll bar settings based on the total text height
AdjustBars( window );
// finally! show the document window
ShowWindow( window );
cleanup:
if ( err != noErr )
{
ErrorAlert( err );
}
return err;
}
void DestroyWindow( WindowRef window )
{
DocumentHandle hDocument;
short menuID;
hDocument = GetWindowDocument(window);
// destroy the WE record
WEDispose( (*hDocument)->we );
// destory the LongControl records attached to the scroll bars
LCDetach( (*hDocument)->scrollBars[ v ] );
LCDetach( (*hDocument)->scrollBars[ h ] );
// dispose of the file alias, if any
ForgetHandle( & ((*hDocument)->fileAlias) );
// destroy the window record and all associated data structures
DisposeWindow( window );
// finally, dispose of the document record
DisposeHandle( (Handle) hDocument );
// adjust the menus to suit
for ( menuID = kMenuFont; menuID <= kMenuFeatures; menuID++ )
DisableItem( GetMenuHandle( menuID ), 0 );
InvalMenuBar();
}